/*************************************************************************
  > File Name: serverApp.hh
  > Author: zj.tian
  > Mail: t657645537@126.com 
  > Created Time: 2015年02月02日 星期一 17时51分44秒
 ************************************************************************/
#ifndef NETWORK_SERVERAPP
#define NETWORK_SERVERAPP

#include "TimeHandler.hh"
#include "clientSession.hh"
#include <map>
#include <utility>
#include <iostream>
#include <boost/make_shared.hpp>
using namespace std;

class serverApp 
	// public boost::enable_shared_from_this<clientSession>
{
	typedef boost::shared_ptr<clientSession> session_ptr;
	typedef boost::shared_ptr<tcp::acceptor> acceptor_ptr;
	typedef map<string,session_ptr>			 SessionMapType;
	public:
		serverApp(boost::asio::io_service& ioservice,tcp::endpoint& endpoint,
				const string& theUUID,GetPluginPortFunc theGetPluginPortFunc,
				NetworkCallbackFunc theCallbackFunc):
			myUUID(theUUID),
			myStrand(ioservice),
			myIoService(ioservice),
			myTimeHandler(myIoService),
			myAcceptor(ioservice,endpoint),
			myNetworkCallbackFunc(theCallbackFunc),
			myGetPluginPortFunc(theGetPluginPortFunc)
		{
			session_ptr aSession(new clientSession(myIoService,myNetworkCallbackFunc));
			myAcceptor.async_accept(aSession->socket(),
					boost::bind(&serverApp::handle_accept,this,
						boost::asio::placeholders::error,aSession));

			myTimeHandler.resetTimeOut(5,
					myStrand.wrap(boost::bind(&serverApp::cleanSession,this)));	
		}
		~serverApp()
		{
			mySessionMap.clear();
		}
		// 5s clean session
		void cleanSession()
		{
			cout << "[serverApp::cleanSession];" << endl;
			SessionMapType::iterator aIter = mySessionMap.begin();
			for(;aIter!=mySessionMap.end();++aIter) {
				session_ptr aSession = aIter->second;
				if (!aSession->is_open()) {
					aIter = mySessionMap.erase(aIter);
				}
			}

		}
		void write(const string& theUUID, const string& theStr)
		{
			SessionMapType::iterator aIter = mySessionMap.find(theUUID);
			if (aIter != mySessionMap.end()) {
				session_ptr aSession = aIter->second;
				aSession->write(theStr);
			} else {
				tcp::resolver aResolver(myIoService);
				tcp::endpoint aEndPoint(tcp::v4(),myGetPluginPortFunc(theUUID));
				session_ptr aSession(new clientSession(myIoService,myNetworkCallbackFunc));
				tcp::resolver::iterator aEndpointIterator = aResolver.resolve(aEndPoint);
				try {
					// sync connect other plugin
					boost::asio::connect(aSession->socket(),aEndpointIterator);

					// first connect send uuid to othre plugin 
					aSession->sendPluginUUID(myUUID);

					// 
					myStrand.wrap(boost::bind(&serverApp::insertSession,this,theUUID,aSession))();
					// myStrand.wrap(boost::bind(&SessionMapType::insert,&mySessionMap,
					//		SessionMapType::value_type(theUUID,aSession)))();
					// mySessionMap.insert(SessionMapType::value_type(theUUID,aSession));	

					// 
					cout << "[serverApp::serverApp] connect success!" << endl;
				} catch (boost::system::system_error& e) {
					DEBUG_ERROR("[serverApp::serverApp] connect error = " << e.what());
				}
			}
		}

	private:
		void handle_accept(const boost::system::error_code& error,session_ptr theSession)
		{
			if (!error) {
				theSession->start();
				myStrand.wrap(boost::bind(&serverApp::insertSession,this,theSession->getUUID(),theSession))();
				// myStrand.wrap(boost::bind(&std::map::insert,&mySessionMap,
				// 		SessionMapType::value_type(theSession->getUUID(),theSession)))();
			} else {
				/*myAcceptor.async_accept(socket(),
						boost::bind(&serverApp::handle_accept,this,
							boost::asio::placeholders::error)); */
			}
			session_ptr aSession(new clientSession(myIoService,this->myNetworkCallbackFunc));
			myAcceptor.async_accept(aSession->socket(),
					boost::bind(&serverApp::handle_accept,this,
						boost::asio::placeholders::error,aSession));
		}
		void insertSession(const string& theUUID, session_ptr theSession)
		{
			mySessionMap.insert(SessionMapType::value_type(theUUID,theSession));
		};

	private:
		GetPluginPortFunc		 myGetPluginPortFunc;
		NetworkCallbackFunc		 myNetworkCallbackFunc;
		boost::asio::io_service& myIoService;
		// all client session
		SessionMapType			 mySessionMap;
		string					 myUUID;
		TimeHandler				 myTimeHandler;
		boost::asio::strand		 myStrand;
		tcp::acceptor			 myAcceptor;
};

#endif

